﻿#region

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ServiceModel;
using System.Threading;
using Radovici.SharedLibrary;
using Stepi.Collections;
using YouTubePlaylist.MusicVideoService;

#endregion

namespace YouTubePlaylist.Models
{
    public class SearchModel : Progressable
    {
        #region Private fields

        private readonly MusicVideoServiceClient _client = new MusicVideoServiceClient(
            new BasicHttpBinding {MaxBufferSize = 2147483647, MaxReceivedMessageSize = 2147483647},
            new EndpointAddress(DataLayer.EndPoint + "MusicVideoService.svc"));

        private readonly Timer _searchTmr;
        private readonly ObservableCollection<VideoPlaylist> _playlists = new ObservableCollection<VideoPlaylist>();        
        private readonly ObservableCollection<Track> _tracks = new ObservableCollection<Track>();
        private readonly ObservableCollection<Track> _userTracks = new ObservableCollection<Track>();
        private ObservableCollection<string> _artists;
        private ObservableCollection<Track> _artistTracks;
        private ObservableCollection<string> _genres;

        private string _lastArtistsRequest;
        private string _lastArtistTracksArtistsRequest;
        private string _lastArtistTracksTracksRequest;
        private string _lastGenresRequest;
        private string _lastTracksRequest;

        #endregion

        public SearchModel()
        {
            _searchTmr = new Timer(OnSearchCallback, null, int.MaxValue, int.MaxValue);

            _client.GetGenresCompleted += OnGetGenresCompleted;
            _client.GetArtistsCompleted += OnGetArtistsCompleted;
            _client.GetArtistTracksCompleted += OnGetArtistTracksCompleted;
            _client.GetTracksCompleted += OnGetTracksCompleted;
            _client.GetUserTracksCompleted += OnGetUserTracksCompleted;
            _client.GetPlaylistsFromSearchCompleted += OnGetPlaylistsFromSearchCompleted;
        }

        public IEnumerable<string> Categories
        {
            get
            {
                return new List<string>(new[]
                                            {
                                                "karaoke", "live", "acoustic", "lyrics", "remix", "cover"
                                            });
            }
        }

        public ObservableCollection<string> Genres
        {
            get { return _genres; }
            private set
            {
                _genres = value;
                Notify("Genres");
            }
        }

        public ObservableCollection<string> Artists
        {
            get { return _artists; }
            private set
            {
                _artists = value;
                Notify("Artists");
            }
        }

        public ObservableCollection<Track> ArtistTracks
        {
            get { return _artistTracks; }
            private set
            {
                _artistTracks = value;
                Notify("ArtistTracks");
            }
        }

        public ObservableCollection<VideoPlaylist> Playlists
        {
            get { return _playlists; }
            private set
            {
                //QUESTION (ER 20101106 asking ER 20090110): Why did I do this? I'm sure there was a reason.
                _playlists.Clear();
                foreach (VideoPlaylist item in value)
                {
                    _playlists.Add(item);
                }
                Notify("Playlists");
                Notify("PlaylistsView");
            }
        }

        public ICollectionView PlaylistsView
        {
            get 
            { 
                return Playlists != null 
                    ? new FilteredCollectionView<VideoPlaylist>(Playlists) //NOTE: Is there something (dynamic filtering mechanism) better than this (now)?
                    : null; 
            }
        }

        public ObservableCollection<Track> Tracks
        {
            get { return _tracks; }
            private set
            {
                _tracks.Clear();
                foreach (Track item in value)
                {
                    _tracks.Add(item);
                }
                Notify("Tracks");
                Notify("TracksView");
            }
        }

        public ICollectionView TracksView
        {
            get 
            {
                return Tracks != null 
                    ? new FilteredCollectionView<Track>(Tracks) 
                    : null; 
            }
        }

        public ObservableCollection<Track> UserTracks
        {
            get { return _userTracks; }
            private set
            {
                _userTracks.Clear();
                foreach (Track item in value)
                {
                    _userTracks.Add(item);
                }
                Notify("UserTracks");
                Notify("UserTracksView");
            }
        }

        public ICollectionView UserTracksView
        {
            get 
            {
                return UserTracks != null 
                    ? new FilteredCollectionView<Track>(UserTracks) 
                    : null;
            }
        }

        private string LastArtistTracksSearchString
        {
            get
            {
                if (_lastArtistTracksArtistsRequest != null && _lastArtistTracksTracksRequest != null)
                {
                    return string.Join(",", new[] { _lastArtistTracksArtistsRequest, _lastArtistTracksTracksRequest });
                }
                return null;
            }
        }

        #region Progressable overrides

        public override string Status
        {
            get { return StatusBarMessages.Count > 0 ? StatusBarMessages[StatusBarMessages.Count - 1] : string.Empty; }
        }

        #endregion

        public void GetGenres(string genre)
        {
            InitializeSearchTimer(delegate { _lastGenresRequest = genre; });
        }

        private void OnGetGenresCompleted(object sender, GetGenresCompletedEventArgs e)
        {
            using (var state = e.UserState as ProgressableState)
            {
                if (e.Error == null && (string) state.State == _lastGenresRequest)
                {
                    Genres = e.Result;
                }
            }
        }

        public void GetArtists(string artist)
        {
            InitializeSearchTimer(delegate
                                      {
                                          _lastArtistsRequest = artist;
                                          //_lastArtistTracksArtistsRequest = artist;
                                          //_lastArtistTracksTracksRequest = string.Empty;
                                      });
        }

        private void OnGetArtistsCompleted(object sender, GetArtistsCompletedEventArgs e)
        {
            using (var state = e.UserState as ProgressableState)
            {
                if (e.Error == null && (string) state.State == _lastArtistsRequest)
                {
                    Artists = e.Result;
                }
            }
        }

        public void GetArtistTracks(string artist, string track)
        {
            InitializeSearchTimer(delegate
                                      {
                                          _lastArtistTracksArtistsRequest = artist;
                                          _lastArtistTracksTracksRequest = track;
                                      });
        }

        private void OnGetArtistTracksCompleted(object sender, GetArtistTracksCompletedEventArgs e)
        {
            using (var state = e.UserState as ProgressableState)
            {
                if (e.Error == null && (string) state.State == LastArtistTracksSearchString)
                {
                    ArtistTracks = e.Result;
                }
            }
        }

        public void GetTracks(string track)
        {
            InitializeSearchTimer(delegate { _lastTracksRequest = track; });
        }

        private void InitializeSearchTimer(Action initializeVariable)
        {
            _lastGenresRequest = null;
            _lastArtistsRequest = null;
            _lastArtistTracksArtistsRequest = null;
            _lastArtistTracksTracksRequest = null;
            _lastTracksRequest = null;
            initializeVariable();
            _searchTmr.Change(250, int.MaxValue);
        }

        private void OnSearchCallback(object state)
        {
            Context.Send(delegate
                             {
                                 User user = DataLayer.Singleton.User;
                                 if (_lastGenresRequest != null)
                                 {
                                     _client.GetGenresAsync(user, _lastGenresRequest,
                                                            new ProgressableState(this, _lastGenresRequest,
                                                                                  string.Format(
                                                                                      "Searching database for genre '{0}'",
                                                                                      _lastGenresRequest)));
                                 }
                                 if (_lastArtistsRequest != null)
                                 {
                                     _client.GetArtistsAsync(user, _lastArtistsRequest,
                                                             new ProgressableState(this, _lastArtistsRequest,
                                                                                   string.Format(
                                                                                       "Searching database for artist '{0}'",
                                                                                       _lastArtistsRequest)));
                                 }
                                 if (LastArtistTracksSearchString != null)
                                 {
                                     _client.GetArtistTracksAsync(user, _lastArtistTracksArtistsRequest,
                                                                  _lastArtistTracksTracksRequest,
                                                                  new ProgressableState(this,
                                                                                        LastArtistTracksSearchString,
                                                                                        string.Format(
                                                                                            "Searching database for artist's track '{0}'",
                                                                                            LastArtistTracksSearchString)));
                                 }
                                 if (_lastTracksRequest != null)
                                 {
                                     string searchString = string.IsNullOrEmpty(_lastTracksRequest)
                                                               ? "everything"
                                                               : _lastTracksRequest;
                                     _client.GetTracksAsync(user, _lastTracksRequest,
                                                            new ProgressableState(this, _lastTracksRequest,
                                                                                  string.Format(
                                                                                      "Searching music database for {0}",
                                                                                      searchString)));
                                     _client.GetUserTracksAsync(user, _lastTracksRequest,
                                                                new ProgressableState(this, _lastTracksRequest,
                                                                                      string.Format(
                                                                                          "Searching user database for {0}",
                                                                                          searchString)));
                                     _client.GetPlaylistsFromSearchAsync(user, _lastTracksRequest,
                                                                         new ProgressableState(this, _lastTracksRequest,
                                                                                               string.Format(
                                                                                                   "Searching playlist database for '{0}'",
                                                                                                   searchString)));
                                 }
                             }, null);
        }

        private void OnGetTracksCompleted(object sender, GetTracksCompletedEventArgs e)
        {
            using (var state = e.UserState as ProgressableState)
            {
                if (e.Error == null && (string) state.State == _lastTracksRequest)
                {
                    Tracks = e.Result;
                }
            }
        }

        private void OnGetUserTracksCompleted(object sender, GetUserTracksCompletedEventArgs e)
        {
            using (var state = e.UserState as ProgressableState)
            {
                if (e.Error == null && (string) state.State == _lastTracksRequest)
                {
                    UserTracks = e.Result;
                }
            }
        }

        private void OnGetPlaylistsFromSearchCompleted(object sender, GetPlaylistsFromSearchCompletedEventArgs e)
        {
            using (var state = e.UserState as ProgressableState)
            {
                if (e.Error == null && (string) state.State == _lastTracksRequest)
                {
                    Playlists = e.Result;
                }
            }
        }
    }
}